////////////////////////////////////////////////////////////////////
//                                                                //
//  Klasse GameFenster                                            //
//                                                                //
//  Funktion: Bereitstellung eines Fensters mit Schaltflchen und //
//     und Textfeldern zur Bedienung sowie einer Flche zur       //
//     grafischen Darstellung der Bewegungssimulation             //
//                                                                //
//  Instanzvariablen:                                             //
//     population / Eine Instanz von class Game                   //
//     start, stop, zufall, loeschen / Schaltflchen              //
//     klein, mittel, gross / Checkboxen                          //
//     xField, yField, anzahlField, geschwField/ TextFelder       //
//     groesse/ Multiplikator fr die Darstellungsgroesse         //
//     warteZeit/ Zahl der Milisekunden zwischen dem              //
//        Generationswechsel                                      //
//     t/ Thread - wird bentigt, damit die Vernderung in der    //
//        Matrix zeitlich gesehen visuell berschaubar bleibt     //
//     anzeigeText/ enthlt den Text, der bei mouseMove ber      //
//        Schaltflchen und Textfelder angezeigt wird             //
//     abbruch/ enthlt true, wenn die Simulation gestopt wird    //
//     anzeige/ true, wenn sich der anzeigeText ndert und neu    //
//        geschrieben werden soll                                 //
//     setzen/ true, wenn per MouseClick eine Zelle belebt wird   //
//        false, wenn eine Zelle per MouseClick gettet wird      //
//                                                                //
//  Verwendet von: class starteGame                               //
//                                                                //
//  Verwendete Klassen: Game                                      //
//                                                                //
//  Autor:  Nina Schumer          Datum:  04.09.2001              //
//                                                                //
////////////////////////////////////////////////////////////////////


import java.awt.*;
import java.awt.event.*;


public class GameFenster extends Frame 
   implements ActionListener, WindowListener, ItemListener,
   MouseListener, MouseMotionListener, Runnable{


   /* ----- Instanzvariablen ----- */

   Game population;
   Button start, stop, zufall, loeschen;
   Checkbox klein, mittel, gross;
   TextField xField, yField, anzahlField, geschwField;

   int groesse=4;	// aufloesung
   int warteZeit=1000;  
   Thread t;		
			
   String anzeigeText="Game of Life";
   boolean abbruch=true, anzeige=false, setzen=true;


   /* ----- Konstruktor ----- */

   public GameFenster(int n, int m, int anzahl)
   /////////////////////////////////////////////////////////////////
   //
   //  Funktion: Konstruktor, erstellt ein neues Fenster mit
   //     Bedienungselementen
   //
   //  Parameter:
   //     n, m / Laenge und Breite des Feldes
   //     anzahl / Anzahl der per Zufall erstellten lebenden
   //        Zellen
   //
   //  Aufgerufen von: class starteGame
   //
   //  Verwendung folgender globaler Variablen: population, start,
   //     stop, zufall, loeschen, klein, mittel, gross, xField, yField,
   //     anzahlField, geschwField
   //
   //  Benutzte Methoden: 
   //     in class Game: Konstruktor
   //
   /////////////////////////////////////////////////////////////////
   {
      super("Game of Life");

   // erzeuge eine neue Population:
      population = new Game(n, m, anzahl);   

   // graphische Daten des Fensters:
      setBackground(Color.lightGray);
      setLocation(200,20);
      setzeFensterGroesse();
      setLayout(new FlowLayout());

   // erzeuge Schaltflchen:          
      start = new Button("Start");
      start.setActionCommand("start");
      start.addActionListener(this);
      start.addMouseMotionListener(this);

      stop = new Button("Stop");
      stop.setActionCommand("stop");
      stop.addActionListener(this);
      stop.addMouseMotionListener(this);

      zufall = new Button("Zufall");
      zufall.setActionCommand("zufall");
      zufall.addActionListener(this);
      zufall.addMouseMotionListener(this);

      loeschen = new Button("Lschen");
      loeschen.setActionCommand("lschen");
      loeschen.addActionListener(this);
      loeschen.addMouseMotionListener(this);

      add(start);
      add(stop);
      add(zufall);
      add(loeschen);
 
   // erstelle eine CheckboxGroup zum Einstellen der Groesse
      CheckboxGroup groesseSchalter = new CheckboxGroup();
      klein = new Checkbox("klein",groesseSchalter,false);
      mittel = new Checkbox("mittel",groesseSchalter,true);
      gross = new Checkbox("gro",groesseSchalter,false);

      klein.addItemListener(this);
      klein.addMouseMotionListener(this);
      mittel.addItemListener(this);
      mittel.addMouseMotionListener(this);
      gross.addItemListener(this);
      gross.addMouseMotionListener(this);
      add(klein);
      add(mittel);
      add(gross);
   
   // erstelle Textfelder fr Geschwindigkeit sowie x, y und anzahl
      xField = new TextField("" + population.n);
      xField.addActionListener(this);
      xField.addMouseMotionListener(this);
      add(xField);

      yField = new TextField("" + population.m);
      yField.addActionListener(this);
      yField.addMouseMotionListener(this);
      add(yField);

      anzahlField = new TextField("" + population.anzahl);
      anzahlField.addActionListener(this);
      anzahlField.addMouseMotionListener(this);
      add(anzahlField);

      geschwField = new TextField(""+warteZeit);
      geschwField.addActionListener(this);
      geschwField.addMouseMotionListener(this);
      add(geschwField);


      addWindowListener(this);
      addMouseListener(this); 
      addMouseMotionListener(this);

      setVisible(true);
   }


   /* ----- Methoden von WindowListener: ----- */

   public void windowClosed(WindowEvent event){}
   public void windowDeiconified(WindowEvent event){}
   public void windowIconified(WindowEvent event){}
   public void windowActivated(WindowEvent event){}
   public void windowDeactivated(WindowEvent event){}
   public void windowOpened(WindowEvent event){}
   public void windowClosing(WindowEvent event){System.exit(0);}

 
   /* ----- Methode von ActionListener ----- */

   public void actionPerformed(ActionEvent event)
   /////////////////////////////////////////////////////////////////
   //
   //  Funktion: Fngt Action-Ereignisse ab, erfragt deren Herkunft
   //     und agiert entsprechend
   //
   //  Parameter: event/ enthlt wichtige Daten ber das Ereignis
   //
   //  Aufgerufen: wenn ein ActionEvent auftritt
   //
   //  Verwendung folgender globaler Variablen: population
   //     xField, yField, anzahlField, geschwField, warteZeit,
   //     anzeigeText, abbruch
   //
   //  Benutzte Methoden: setzeFensterGroesse(), startAnimation()
   //     in class Game: Konstruktor, getGeneration(), randomMatrix(),
   //                    initializeGeneration()
   //
   /////////////////////////////////////////////////////////////////
   {
      Object obj = event.getSource();
      if (obj instanceof TextField) {
         TextField tf = (TextField)obj;
         if (tf==geschwField) 
         {
            try
            {
               warteZeit=Integer.parseInt(((TextField)obj).getText());
            }
            catch (Exception e)
            { 
               anzeigeText="Nur Ziffern bitte";
               anzeige=true;
               repaint();
            }
         } else if (tf==xField) {
            try { 
               int zahl = Integer.parseInt(tf.getText());
               if (zahl<5) zahl=5;
               if (zahl>200) zahl=200;
               population= new Game(zahl, population.m, population.anzahl);
               population.anzahl= (int)((population.n*population.m)/3);
               anzahlField.setText(""+population.anzahl);
               tf.setText(""+zahl);
               setzeFensterGroesse(); setVisible(false);
               setTitle("Game of Life   -   "+ population.getGeneration()+". Generation");
               repaint(); setVisible(true);
            }
            catch (Exception e) {anzeigeText="Nur Ziffern bitte"; anzeige=true; repaint();}
         } else if (tf==yField) {
            try {
               int zahl = Integer.parseInt(tf.getText()); 
               if (zahl<5) zahl=5;
               if (zahl>200) zahl=200;
               population= new Game(population.n, zahl, population.anzahl);
               population.anzahl= (int)((population.n*population.m)/3);
               anzahlField.setText(""+population.anzahl);
               tf.setText(""+zahl);
               setzeFensterGroesse(); setVisible(false);
               setTitle("Game of Life   -   "+ population.getGeneration()+". Generation");
               repaint(); setVisible(true);    
            }
            catch (Exception e) {anzeigeText="Nur Ziffern bitte"; anzeige=true; repaint();}
         } else if (tf==anzahlField) {
            try
            {
               population.anzahl=Integer.parseInt(tf.getText()); 
               if (population.anzahl>=population.n*population.m)
               {
                  population.anzahl=population.n*population.m-1; anzahlField.setText(""+population.anzahl);
               }
            }
            catch(Exception e) {}
         }  
      } else if (obj instanceof Button) {
         String str = event.getActionCommand();
         if (str.equals("start"))
         {
            if (abbruch==true) {abbruch=false; startAnimation();}
            else startAnimation();
         } else if (str.equals("stop"))
         {
            abbruch=true;
         } else if (str.equals("zufall")) 
         {
            population.initializeGeneration();
            population.initializeMatrix();
            population.randomMatrix();
            repaint();
            setTitle("Game of Life   -   "+ population.getGeneration()+". Generation");
         } else if (str.equals("lschen"))
         {  
            population.initializeMatrix();
            population.initializeGeneration();
            repaint();
            setTitle("Game of Life   -   "+ population.getGeneration()+". Generation");
         }

      }      
   }


   /* ----- Methoden von MouseListener ----- */

   public void mousePressed(MouseEvent event)
   /////////////////////////////////////////////////////////////////
   //
   //  Funktion: Belebt oder lscht bei Mausclick eine Zelle
   //
   //  Parameter: event/ enthlt wichtige Daten ber das Maus-Ereignis
   //
   //  Aufgerufen: wenn ein MouseClick auftritt
   //
   //  Verwendung folgender globaler Variablen: setzen, groesse, population
   //
   //  Benutzte Methoden: 
   //     in class Game: isSet(int, int), setXY(int, int), deleteXY(int, int)
   //
   ///////////////////////////////////////////////////////////////////
   {
      int x = (int)( (float)population.n/2 - ((float)getSize().width/2- (float)event.getX() ) / (float)groesse );
      int y = (int) ( (float)(event.getY() -70)/ (float)groesse );
      setzen=!population.isSet(x,y); 
      if (setzen) population.setXY(x,y); else population.deleteXY(x,y);
      repaint();
   }

   public void mouseReleased(MouseEvent event){}
   public void mouseClicked(MouseEvent event){}
   public void mouseEntered(MouseEvent event){}
   public void mouseExited(MouseEvent event){}


   /* ----- Methoden von MouseMotionListener ----- */

   public void mouseMoved(MouseEvent event)
   ///////////////////////////////////////////////////////////////////
   //
   //  Funktion: Zeigt bei MouseMove ber ein Bedienungselement einen
   //            erlklrenden Kommentar an
   //
   //  Parameter: event/ enthlt wichtige Daten ber das Maus-Ereignis
   //
   //  Aufgerufen: wenn ein MouseMove auftritt
   //
   //  Verwendung folgender globaler Variablen: xField, yField,
   //     anzahlField, geschwField, start, stop, zufall, loeschen,
   //     klein, mittel, gross, anzeigeText
   //
   //  Benutzte Methoden: keine
   //
   ///////////////////////////////////////////////////////////////////
   {
      String anzeigeText=this.anzeigeText;
      Object obj= event.getComponent();

      if (obj instanceof TextField) {
         TextField tf = (TextField)obj;
         if (tf==geschwField) anzeigeText="Wartezeit zwischen zwei Generationen";
         else if (tf==xField) anzeigeText="Breite des Feldes";
         else if (tf==yField) anzeigeText="Hhe des Feldes";
         else if (tf==anzahlField) anzeigeText="Anzahl der zufllig lebenden Zellen";
      } else if (obj instanceof Button) {
         Button but = (Button)obj;
         if (but==start) anzeigeText="Startet die Simulation";
         else if (but==stop) anzeigeText="Stoppt die Simulation";
         else if (but==zufall) anzeigeText="Erstellt zufllig eine neue Population";
         else if (but==loeschen) anzeigeText="Ttet alle lebenden Zellen";
      } else if (obj instanceof Checkbox) {
         Checkbox box = (Checkbox)obj;
         if (box==klein) anzeigeText="kleine Darstellung";
         else if (box==mittel) anzeigeText="mittlere Darstellung";
         else if (box==gross) anzeigeText="groe Darstellung";
      } else anzeigeText="";
      // wenn sich der AnzeigeText gendert hat, so lsche den alten
      // und zeige den neuen an
      if (anzeigeText!=this.anzeigeText) {
         this.anzeigeText=anzeigeText;
         anzeige=true;
         repaint();
      }
   }


   public void mouseDragged(MouseEvent event)
   ///////////////////////////////////////////////////////////////////
   //
   //  Funktion: Belebt oder lscht bei Ziehen der Maus Zellen
   //
   //  Parameter: event/ enthlt wichtige Daten ber das Maus-Ereignis
   //
   //  Aufgerufen: wenn die Maus bei gedrckter Taste bewegt wird
   //
   //  Verwendung folgender globaler Variablen: setzen, groesse, population
   //
   //  Benutzte Methoden: 
   //     in class Game: setXY(int, int), deleteXY(int, int)
   //
   ///////////////////////////////////////////////////////////////////
   {
      int x = (int)( (float)population.n/2 - ((float)getSize().width/2- (float)event.getX() ) / (float)groesse );
      int y = (int) ( (float)(event.getY() -70)/ (float)groesse );
  
      if (setzen) population.setXY(x,y); else population.deleteXY(x,y);
      repaint();
   }

       
   /* ----- Methode von ItemListener ----- */

   public void itemStateChanged(ItemEvent event) 
   ///////////////////////////////////////////////////////////////////
   //
   //  Funktion: Fngt Item-Ereignisse ab, erfragt deren Herkunft und
   //     agiert entsprechend
   //
   //  Parameter: event/ enthlt wichtige Daten ber das Ereignis
   //
   //  Aufgerufen: wenn ein ItemEvent auftritt (Click auf Checkbox)
   //
   //  Verwendung folgender globaler Variablen: groesse
   //     
   //  Benutzte Methoden: setzeFensterGroesse()
   //
   ///////////////////////////////////////////////////////////////////
   {
      Checkbox cb = (Checkbox) event.getItemSelectable();
      String str = cb.getLabel(); 
      if (str.equals("klein")) 
      {
          groesse=2;
          setzeFensterGroesse();      }
      else if (str.equals("mittel"))
      {
          groesse=4;
          setzeFensterGroesse();      }
      else if (str.equals("gro")) 
      {
          groesse=8;
          setzeFensterGroesse();
      }
   }


   public void startAnimation()
   ///////////////////////////////////////////////////////////////////
   //
   //  Funktion: Startet den Thread, welcher fr die Simulation
   //     verantwortlich ist
   //
   //  Parameter: keine
   //
   //  Aufgerufen von: actionPerformed(ActionEvent) bei Start-Button-
   //     Bettigung
   //
   //  Verwendung folgender globaler Variablen: t
   //     
   //  Benutzte Methoden: keine (automatischer Aufruf von run())
   //
   ///////////////////////////////////////////////////////////////////
   {
      t = new Thread(this);
      t.start();
   }


   public void run()
   ///////////////////////////////////////////////////////////////////
   //
   //  Funktion: Startet den Thread, welcher fr die Simulation
   //     verantwortlich ist
   //
   //  Parameter: keine
   //
   //  Aufgerufen von: automatisch aufgerufen bei Start des Threads
   //
   //  Verwendung folgender globaler Variablen: population, t, abbruch,
   //     warteZeit
   //     
   //  Benutzte Methoden: 
   //     in class Game: nextGeneration(), getGeneration()
   //
   ///////////////////////////////////////////////////////////////////
   {
     
      do {
         population.nextGeneration();
         repaint();
         setTitle("Game of Life   -   "+ population.getGeneration()+". Generation");
         try
         {
            t.sleep(warteZeit);
         }
         catch(InterruptedException e) { }
      } while (!abbruch);
  }


   /*  Methoden fr die graphische Darstellung  */
 
   private void setzeFensterGroesse()
   ///////////////////////////////////////////////////////////////////
   //
   //  Funktion: Setzt die Fenstergroesse in Abbhngigkeit von Feldgroesse
   //     und der Darstellungsgroesse
   //
   //  Parameter: keine
   //
   //  Aufgerufen von: GameFenster-Konstruktor, actionPerformed(ActionEvent),
   //     itemStateChanged(ItemEvent)
   //
   //  Verwendung folgender globaler Variablen: population, groesse
   //     
   //  Benutzte Methoden: keine
   //
   ///////////////////////////////////////////////////////////////////
   {
      int x= population.n*groesse+40;
      if (x<600) x=600;
      setSize(x, population.m*groesse+100); 
   }


   public void paint(Graphics g)
   ///////////////////////////////////////////////////////////////////
   //
   //  Funktion: Funktion fr die grafische Darstellung
   //
   //  Parameter: g/ stellt die grafischen Methoden zur Verfgung
   //
   //  Aufgerufen: -> indirekt durch repaint() in actionPerformed(ActionEvent),
   //     mousePressed(MouseEvent), mouseDragged(MouseEvent), run()
   //     -> direkt durch update()
   //
   //  Verwendung folgender globaler Variablen: population, groesse,
   //     anzeige, anzeigeText
   //     
   //  Benutzte Methoden: keine
   //
   ///////////////////////////////////////////////////////////////////
   {

      // Die aktuelle Generation wird gezeichnet
      for (int i=0; i<population.n; i++)
      {
         for (int j=0; j<population.m; j++)
         {
            if (population.feld[i][j][0]==true) g.setColor(Color.red);
               else g.setColor(Color.black);
            g.fillRect(getSize().width/2- (population.n/2-i)*groesse,70+groesse*j,groesse,groesse);
         }
      }

      // Kommentare zu Buttons werden abgegeben
      if (anzeige)
      {
         g.setColor(Color.gray);  
         g.drawString(anzeigeText,getSize().width/2-100,65);
         anzeige=false;
      }
   }


   public void update(Graphics g)
   ///////////////////////////////////////////////////////////////////
   //
   //  Funktion: Diese Methode wird berlagert, damit repaint() ohne
   //     flackern erfolgt. Es wird nur der Teil auf dem Bildschirm
   //     gelscht, der wirklich gelscht werden mu
   //
   //  Parameter: g/ stellt die grafischen Methoden zur Verfgung
   //
   //  Aufgerufen: durch repaint() in actionPerformed(ActionEvent),
   //     mousePressed(MouseEvent), mouseDragged(MouseEvent), run()
   //
   //  Verwendung folgender globaler Variablen: population, groesse,
   //     anzeige,
   //     
   //  Benutzte Methoden: keine
   //
   ///////////////////////////////////////////////////////////////////
   {	
      // Lsche gegebenfalls den alten Kommentar
      if (anzeige) 
      {
         g.setColor(Color.lightGray);   
         g.fillRect(getSize().width/2-100,55,200,15);
      }
      // Aufruf der eigentlichen Paint-Methode
      paint(g);
   }


}
